﻿using System;
using System.Diagnostics;
using System.IO;

namespace Pegasus.Diagnostics.Contracts
{
	/// <summary>
	/// This is a copy-cat class for Microsoft Code Contract project.  The microsoft code has some issue with WCF
	/// and IIS, when they get them resolved then we can just swap them in.
	/// </summary>
	public static class Contract
	{
		/// <summary>
		/// Method requires the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		public static void Requires( bool condition )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Precondition, null );
			}
		}

		/// <summary>
		/// Method requires the specified condition.
		/// </summary>
		/// <typeparam name="TException">The type of the exception.</typeparam>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		public static void Requires<TException>( bool condition ) 
			where TException : Exception
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Precondition, null );
			}
		}

		/// <summary>
		/// Method requires the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		/// <param name="userMessage">The user message.</param>
		public static void Requires( bool condition, string userMessage )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Precondition, userMessage );
			}
		}

		/// <summary>
		/// Asserts the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		public static void Assert( bool condition )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Assert, null );
			}
		}
		
		/// <summary>
		/// Asserts the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		/// <param name="userMessage">The user message.</param>
		public static void Assert( bool condition, string userMessage )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Assert, userMessage );
			}
		}

		/// <summary>
		/// Assumes the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		public static void Assume( bool condition )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Assume, null );
			}
		}

		/// <summary>
		/// Assumes the specified condition.
		/// </summary>
		/// <param name="condition">if set to <c>true</c> [condition].</param>
		/// <param name="userMessage">The user message.</param>
		public static void Assume( bool condition, string userMessage )
		{
			if( !condition )
			{
				ReportFailure( ContractFailureKind.Assume, userMessage );
			}
		}

		/// <summary>
		/// Reports the failure.
		/// </summary>
		/// <param name="failureKind">Kind of the failure.</param>
		/// <param name="userMessage">The user message.</param>
		private static void ReportFailure( ContractFailureKind failureKind, string userMessage )
		{
			string faultReference = GetReferencePoint( new StackFrame( 2, true ) );
			string falultMessage = string.Format( "Code Contract {0} Failure, {1}", failureKind.ToString(), faultReference );

			ContractException exception = new ContractException( failureKind, userMessage );

			if( ContractFailed != null )
			{
				ContractFailed( null, new ContractFailedEventArgs( failureKind, userMessage ) );
			}

			throw exception;
		}

		/// <summary>
		/// Gets the calling method.
		/// </summary>
		/// <param name="stackFrame">The stack frame.</param>
		/// <returns></returns>
		private static string GetReferencePoint( StackFrame stackFrame )
		{
			string filename = Path.GetFileNameWithoutExtension( stackFrame.GetFileName() );
			return string.Format( "{0}:{1}", filename, stackFrame.GetFileLineNumber() );
		}

		/// <summary>
		/// Occurs when a contract condition fails.
		/// </summary>
		public static event EventHandler<ContractFailedEventArgs> ContractFailed;
	}
}

